package com.miyake.dsp.osa.lab.ui;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import com.miyake.dsp.osa.lab.LabUiAnnotation;
import com.miyake.dsp.osa.lab.SettingPanelListener;

public class SettingPanelUI extends JPanel {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1033890777497039616L;
	private List<SettingPanelListener> listeners = new LinkedList<SettingPanelListener>();
	private Object setting;
	
	public void addSettingPanelListener(SettingPanelListener listener) {
		listeners.add(listener);
	}
	
	protected void fireOnSet() {
		for (SettingPanelListener listener : listeners) {
			listener.onSet();
		}
	}
	
	public SettingPanelUI(Object o, SettingPanelListener listener) {
		this.setting = o;
		addSettingPanelListener(listener);
		createPanel();
	}
	
	public SettingPanelUI(Object o) {
		this.setting = o;
		createPanel();
	}	
	
	private Map<Field, JComponent> map;
	
	protected void createPanel() {
		this.setBorder(new TitledBorder("Setting"/*o.getClass().getSimpleName()*/));
		this.setLayout(new VerticalLayout(0, VerticalLayout.LEFT)/*new BoxLayout(this, BoxLayout.Y_AXIS)*/);
		
		map = new HashMap<Field, JComponent>();
		Object o = this.setting;
		
		Class cls = o.getClass();
		Map<String, String> fieldMap = new HashMap<String, String>();
		Field[] fields = cls.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			String fieldName = fields[i].getName();
			String fieldType = fields[i].getType().getName();
			fieldMap.put(fieldName, fieldType);
			
			JPanel p = new JPanel();
			p.setLayout(new FlowLayout(FlowLayout.LEFT));
			String caption = fieldName;
			boolean visible = false;
			for (int j = 0; j < fields[i].getAnnotations().length; j++) {
				Annotation a = fields[i].getAnnotations()[j];
				if (a.annotationType().getName().contains("LabUiAnnotation")) {
					caption = (((LabUiAnnotation)a).caption());
					visible = true;
				}
			}
			
			if (!visible) {
				continue;
			}
			
			p.add(new JLabel(caption));
			
			if (fieldType.equals("java.lang.String")) {
				p.setLayout(new GridLayout(2, 1));
				JTextField text = new JTextField((String)getPrivateField(cls, o, fieldName));
				text.setPreferredSize(new Dimension(150, 24));
				p.add(text);
				map.put(fields[i], text);
			}
			else if (fieldType.equals("double")||fieldType.equals("java.lang.Double")) {
				JTextField  text = new JTextField(String.valueOf(
						(Double)getPrivateField(cls, o, fieldName)
						));
				p.add(text);	
				text.setPreferredSize(new Dimension(70, 24));
				map.put(fields[i], text);
			}
			else if (fieldType.equals("int")||fieldType.equals("java.lang.Integer")) {
				JTextField  text = new JTextField(String.valueOf(
						(Integer)getPrivateField(cls, o, fieldName)
						));
				p.add(text);				
				text.setPreferredSize(new Dimension(70, 24));
				map.put(fields[i], text);
			}	
			else if (fieldType.equals("float")||fieldType.equals("java.lang.Float")) {
				JTextField  text = new JTextField(String.valueOf(
						(Float)getPrivateField(cls, o, fieldName)
						));
				p.add(text);				
				text.setPreferredSize(new Dimension(70, 24));
				map.put(fields[i], text);
			} 
			else {
				this.createComboBox(map, p, fields[i], getPrivateField(cls, o, fieldName));
			}
			add(p);
			
		}
		
		createCustomComponent(o);
		
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
		JButton button = new JButton("Set");
		panel.add(button);
		add(panel);
		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				writeToObject();
				fireOnSet();
			}
			
		});
		this.updateUI();
	}
	
	protected void createCustomComponent(Object o) {
		// This method should be implemented by sub classes.
	}

	protected void storeObject(Field field, JComponent component, Object object) {}

	protected JComboBox createComboBox(Map<Field, JComponent> map, JPanel p, Field fields2, Object object) {
		Field[] fields = fields2.getType().getDeclaredFields();
		JComboBox combo = new JComboBox();
		map.put(fields2, combo);
		p.add(combo);
		for (int i = 0; i < fields.length-1; i++) {
			combo.addItem(fields[i].getName());
		}
		combo.setSelectedItem(object.toString());
		
		return combo;
	}
	
	protected void storeComboBox(Field field, JComponent component, Object object)
			throws IllegalAccessException {
		Class cls = field.get(object).getClass();
		JComboBox combo = (JComboBox)component;
		field.set(object, cls.getEnumConstants()[combo.getSelectedIndex()]);
	}
	
	protected Object getPrivateField(Class<?> cls, Object obj,
	        String fieldName) {
	    Object result = null;

	    do {
	        try {
	            Field field = cls.getDeclaredField(fieldName);
	            field.setAccessible(true);
	            result = field.get(obj);
	            break;
	        } catch (NoSuchFieldException e) {
	            cls = cls.getSuperclass();
	        } catch (Exception e) {
	            e.printStackTrace();
	            break;
	        }
	    } while (cls != null);

	    return result;
	}

	protected void writeToObject() {
		Object o = this.setting;
		for (Field field : map.keySet()) {
			field.setAccessible(true);
			try {
				if (field.getType().getName().equals("double")||field.getType().getName().equals("java.lang.Double")) {
					field.setDouble(o, Double.valueOf(((JTextField)map.get(field)).getText()).doubleValue());
				}
				else if (field.getType().getName().equals("int")) {
					field.setInt(o, Integer.valueOf(((JTextField)map.get(field)).getText()).intValue());
				}
				else if (field.getType().getName().equals("float")||field.getType().getName().equals("java.lang.Float")) {
					field.setFloat(o, Float.valueOf(((JTextField)map.get(field)).getText()).floatValue());
				}
				else if (field.getType().getName().equals("java.lang.Integer")) {
					field.set(o, Integer.valueOf(((JTextField)map.get(field)).getText()));
				}
				else if (field.getType().getName().equals("java.lang.Double")) {
					field.set(o, Double.valueOf(((JTextField)map.get(field)).getText()));
				}
				else if (field.getType().getName().equals("java.lang.Float")) {
					field.set(o, Float.valueOf(((JTextField)map.get(field)).getText()));
				}
				else if (field.getType().getName().equals("java.lang.String")) {
					field.set(o, ((JTextField)map.get(field)).getText());							
				}
				else {
					storeComboBox(field, map.get(field), o);
					//storeObject(field, map.get(field), o);
				}
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
}
